package drds.common.db.xml_mapping.executor.loader;

import drds.common.db.xml_mapping.executor.Executor;
import drds.common.db.xml_mapping.executor.ExecutorException;
import drds.common.db.xml_mapping.executor.ResultExtractor;
import drds.common.db.xml_mapping.mapping.BindSql;
import drds.common.db.xml_mapping.mapping.Environment;
import drds.common.db.xml_mapping.mapping.Mapper;
import drds.common.db.xml_mapping.reflection.factory.ObjectFactory;
import drds.common.db.xml_mapping.session.Configuration;
import drds.common.db.xml_mapping.session.ExecutorType;
import drds.common.db.xml_mapping.session.RowBounds;
import drds.common.db.xml_mapping.transaction.Transaction;
import drds.common.db.xml_mapping.transaction.TransactionFactory;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;


public class ResultLoader {

  protected final Configuration configuration;
  protected final Executor executor;
  protected final Mapper MappedStatement;
  protected final Object parameterObject;
  protected final Class<?> targetType;
  protected final ObjectFactory objectFactory;

  protected final BindSql bindSql;
  protected final ResultExtractor resultExtractor;
  protected final long creatorThreadId;


  protected Object resultObject;

  public ResultLoader(Configuration configuration, Executor executor, Mapper MappedStatement, Object parameterObject, Class<?> targetType, BindSql bindSql) {
    this.configuration = configuration;
    this.executor = executor;
    this.MappedStatement = MappedStatement;
    this.parameterObject = parameterObject;
    this.targetType = targetType;
    this.objectFactory = this.configuration.getObjectFactory();

    this.bindSql = bindSql;
    this.resultExtractor = new ResultExtractor(this.configuration, objectFactory);
    this.creatorThreadId = Thread.currentThread().getId();
  }

  public Object loadResult() throws SQLException {
    List<Object> list = selectList();
    resultObject = resultExtractor.extractObjectFromList(list, targetType);
    return resultObject;
  }

  private <E> List<E> selectList() throws SQLException {
    Executor executor = this.executor;
    if (Thread.currentThread().getId() != this.creatorThreadId || executor.isClosed()) {
      executor = newExecutor();
    }
    try {
      return executor.query(MappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, bindSql);
    } finally {
      if (executor != this.executor) {
        executor.close(false);
      }
    }
  }

  private Executor newExecutor() throws SQLException {
    final Environment environment = configuration.getEnvironment();
    if (environment == null) {
      throw new ExecutorException("ResultLoader could not load lazily.  Environment was not configured.");
    }
    final DataSource dataSource = environment.getDataSource();
    if (dataSource == null) {
      throw new ExecutorException("ResultLoader could not load lazily.  DataSource was not configured.");
    }
    final TransactionFactory transactionFactory = environment.getTransactionFactory();
    final Transaction executor = transactionFactory.newTransaction(dataSource.getConnection(), false);
    return configuration.newExecutor(executor, ExecutorType.SIMPLE);
  }

  public boolean wasNull() {
    return resultObject == null;
  }

}
